package com.rightrule.rule.executor;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rightrule.rule.compile.Executable;
import com.rightrule.rule.constans.*;
import com.rightrule.rule.dto.DecisionFieldDTO;
import com.rightrule.rule.dto.DecisionRuleDTO;
import com.rightrule.rule.dto.DecisionRuleWaterDTO;
import com.rightrule.rule.executor.node.BaseNode;
import com.rightrule.rule.executor.node.NormalNode;
import com.rightrule.rule.response.BaseExecutionResponse;
import com.rightrule.rule.response.FlowExecutionResponse;
import com.rightrule.rule.response.NodeExecutionResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

@Data
@Slf4j
public class RuleFlow implements Executable {

    /**
     * 自增主键
     */
    private Long id;


    /**
     * 策略id
     */
    private Long strategyId;


    /**
     * 策略版本
     */
    private Long strategyVersionId;


    /**
     * 父规则流id，如果是住规则流测是0
     */
    private Long parentFlowId;


    /**
     * 规则名称，由前端建立节点关系时命名，主规则流默认设置成master，如果是子规则流节点，则是子规则流名称
     */
    private String flowName;


    /**
     * 依赖的输入变量
     */
    List<DecisionFieldDTO> inputField;


    /**
     * 依赖的中间变量
     */
    List<DecisionFieldDTO> eventField;


    /**
     * 输出变量
     */
    Map<String, DecisionFieldDTO> outputFieldMaps;


    /**
     * 中间变量
     */
    Map<String, DecisionFieldDTO> midFieldMaps;


    /**
     * 所有节点(除开始节点以外的)
     */
    private Map<Long, BaseNode> nodes;


    /**
     * 开始节点
     */
    private NormalNode startNode;


    /**
     * 规则执行
     *
     * @param context
     * @param varibleMap
     * @return
     */
    @Override
    public BaseExecutionResponse execute(RuntimeContext context, Map<String, Object> varibleMap) {

        FlowExecutionResponse flowExecutionResponse = new FlowExecutionResponse(context.getSeq());
        ArrayList<DecisionRuleWaterDTO> ruleExeWaters = new ArrayList<>();
        // 所有的输出变量
        HashMap<String, Object> outputMap = new HashMap<>();
        // 所有的命中了的规则
        ArrayList<DecisionRuleDTO> hitRules = new ArrayList<>();

        BaseNode doNode = startNode;
        while (doNode != null) {

            // 如果是开始节点或者合并节点
            if (doNode.getNodeType().equals(RuleFlowNodeTypeEnum.START.getCode())
                    || doNode.getNodeType().equals(RuleFlowNodeTypeEnum.MERGE.getCode())
            ) {
                // 如果下一个节点不为空，则当前执行节点指向下一个节点
                if (!CollectionUtils.isEmpty(doNode.getNextNodes())) {
                    doNode = doNode.getNextNodes().get(0).getNodeInfo();
                }
            }

            // 如果是分支节点,则执行分支节点表达式，如果没有匹配的结果，表示分支节点路由失败
            else if (doNode.getNodeType().equals(RuleFlowNodeTypeEnum.BRANCH.getCode())) {
                NodeExecutionResponse executionResponse = doNode.execute(context, varibleMap);
                // 如果分支节点为匹配失败
                if (CommonConstant.EXECUTE_BRANCH_FAIL.equals(executionResponse.getExecuteStaus())) {
                    flowExecutionResponse.setExecuteStaus(CommonConstant.EXECUTE_BRANCH_FAIL);
                    break;
                }

                // 路由成功，则当前执行节点，指向下一个应该执行的节点
                doNode = doNode.getNextNodes().get(executionResponse.getNextNodeIndex()).getNodeInfo();
            }

            // 如果是执行节点
            else if (doNode.getNodeType().equals(RuleFlowNodeTypeEnum.EXECUTE.getCode())) {
                DecisionRuleWaterDTO ruleWaterDTO = new DecisionRuleWaterDTO();
                NodeExecutionResponse executionResponse = doNode.execute(context, varibleMap);

                // 如果是执行节点匹配失败
                if (CommonConstant.EXECUTE_RULE_FAIL.equals(executionResponse.getExecuteStaus())) {
                    flowExecutionResponse.setExecuteStaus(CommonConstant.EXECUTE_RULE_FAIL);
                    ruleWaterDTO.setStatus("FAIL");
                } else {
                    ruleWaterDTO.setStatus("FINISH");
                }

                // 如果命中规则,将规则加入规则列表
                if (executionResponse.getIsHit()) {
                    ruleWaterDTO.setIsHit(CommonConstant.RULE_HIT);
                    hitRules.add(executionResponse.getExecRule());
                } else {
                    ruleWaterDTO.setIsHit(CommonConstant.RULE_NOT_HIT);
                }

                // 如果当前执行规则输出的变量需要传递给下一个节点使用
                fillOutputData(varibleMap, executionResponse.getOutput(), false);

                // 输出变量汇总
                fillOutputData(outputMap, executionResponse.getOutput(), true);

                // 设置每个执行节点的决策流水
                ruleWaterDTO.setRuleCode(executionResponse.getExecRule().getRuleCode());
                ruleWaterDTO.setRuleld(executionResponse.getExecRule().getId());
                ruleWaterDTO.setRuleName(executionResponse.getExecRule().getRuleName());
                ruleWaterDTO.setOutputResult(JSONObject.toJSONString(executionResponse.getOutput()));
                ruleWaterDTO.setStrategyId(context.getStrategyId());
                ruleWaterDTO.setStrategyVersionid(context.getStrategyVersionId());
                ruleWaterDTO.setBusinessId(context.getBusinessId());
                ruleWaterDTO.setIpId(context.getIpid());
                ruleWaterDTO.setIpRoleId(context.getIpRoleId());

                // 如果是表达式类型的规则命中了
                if (ScriptTypeEnum.Aviator.getCode().equals(executionResponse.getExecRule().getExecuteType())
                        && executionResponse.getIsHit()) {
                    ruleWaterDTO.setIsDefaultResult(CommonConstant.NOT_DEFAULT_OUTPUT);
                }
                // 如果是groovy类型的规则命中了
                else {
                    ruleWaterDTO.setIsDefaultResult(CommonConstant.DEFAULT_OUTPUT);
                }
                ruleExeWaters.add(ruleWaterDTO);

                // 将执行节点的指针指向下一个节点
                if (!CollectionUtils.isEmpty(doNode.getNextNodes())) {
                    doNode = doNode.getNextNodes().get(0).getNodeInfo();
                }
            }
            // todo 子规则流节点
            else if (doNode.getNodeType().equals(RuleFlowNodeTypeEnum.NEST.getCode())) {
                log.info("嵌套规则流节点暂时未实现");
            }
            // 如果是结束节点则直接结束
            else if (doNode.getNodeType().equals(RuleFlowNodeTypeEnum.END.getCode())) {
                break;
            }
        }

        log.info("flowId = {},outputFieldMaps={}", getId(), outputFieldMaps);
        HashMap<String, Object> finalVariableMap = new HashMap<>();
        // 最终输出变量不放到variableMap
        if (parentFlowId == 0) {
            Iterator<Map.Entry<String, Object>> iterator = varibleMap.entrySet().iterator();
            if (iterator.hasNext()) {
                Map.Entry<String, Object> entry = iterator.next();
                if (!outputFieldMaps.containsKey(entry.getKey())) {
                    finalVariableMap.put(entry.getKey(), entry.getValue());
                }
            }
        }

        flowExecutionResponse.setInput(finalVariableMap);
        flowExecutionResponse.setOutput(outputMap);
        flowExecutionResponse.setHitRules(hitRules);
        flowExecutionResponse.setRuleExeWaters(ruleExeWaters);
        log.info("flowId = {},flowResp ={}", getId(), JSONObject.toJSON(flowExecutionResponse));
        return flowExecutionResponse;
    }


    /**
     * 将输出变量传递给下一个节点，或者汇总输出变量
     *
     * @param outputMap
     * @param executeOutputMap
     * @param isFinalOutput
     */
    private void fillOutputData(Map<String, Object> outputMap, Map<String, Object> executeOutputMap, boolean isFinalOutput) {
        Iterator<Map.Entry<String, Object>> item = executeOutputMap.entrySet().iterator();

        // 遍历当前规则执行结果的输出变量
        while (item.hasNext()) {

            // 按顺序拿出当前的单个输出变量
            Map.Entry<String, Object> next = item.next();
            String exeOutputKey = next.getKey();
            String exeOutputValue = String.valueOf(next.getValue());

            // 非输出变量，并且是最终统计输出变量时调用，不需要加入输出变量的列表
            if (!outputFieldMaps.containsKey(exeOutputKey) && isFinalOutput) {
                continue;
            }

            // 历史的规则输出变量不包含当前的规则输出变量，则要加入进去，并且如果执行中的规则没有当前输出变量也会加入进去
            if (!outputMap.containsKey(exeOutputKey)) {
                outputMap.put(exeOutputKey, exeOutputValue);
                continue;
            }

            // 输出变量值和历史的一致，则不需要加入历史的输出变量列表中
            if (String.valueOf(outputMap.get(exeOutputKey)).equals(exeOutputValue)) {
                continue;
            }

            // 需要加入的变量有设置优先级，则优先级比较。判断是否存在优先级标识
            if (outputFieldMaps.containsKey(exeOutputKey)
                    && !StringUtils.isEmpty(outputFieldMaps.get(exeOutputKey).getPriorityType())
            ) {
                String hisOutValue = String.valueOf(outputMap.get(exeOutputKey));
                String enumList = outputFieldMaps.get(exeOutputKey).getEnumList();

                // 枚举或数值类型，可做优先级对比。判断是否是枚举或者数值类型变量
                if (OutputFieldValueTypeEnum.ENUM.getCode().equals(outputFieldMaps.get(exeOutputKey).getValueType())
                        || OutputFieldValueTypeEnum.NUM.getCode().equals(outputFieldMaps.get(exeOutputKey).getValueKey())
                ) {

                    // 优先级按枚举列表排序
                    if (OutputFieldPriorityTypeEnum.ENUL.getCode().equals(outputFieldMaps.get(exeOutputKey).getPriorityType())
                            && !StringUtils.isEmpty(outputFieldMaps.get(exeOutputKey).getEnumList())) {
                        // 当前规则输出变量能覆盖掉历史的规则输出变量
                        if (executeOutputReplaceHisOutput(exeOutputValue, hisOutValue, enumList)) {
                            outputMap.put(exeOutputKey, next.getValue());
                        }
                    }

                    // BIG_NUN类型优先级按照数字大到小排序
                    else if (OutputFieldPriorityTypeEnum.BIG_NUN.getCode().equals(outputFieldMaps.get(exeOutputKey).getPriorityType())) {
                        if (Double.parseDouble(exeOutputValue) > Double.parseDouble(hisOutValue)) {
                            outputMap.put(exeOutputKey, next.getValue());
                        }
                    }

                    // SMALL_NUM类型优先级按照数字由小到大
                    else if (OutputFieldPriorityTypeEnum.SMALL_NUM.getCode().equals(outputFieldMaps.get(exeOutputKey).getPriorityType())) {
                        if (Double.parseDouble(exeOutputValue) < Double.parseDouble(hisOutValue)) {
                            outputMap.put(exeOutputKey, next.getValue());
                        }
                    }
                }
            }
            // 没有设置优先级，则按正常覆盖
            else {
                outputMap.put(exeOutputKey, next.getValue());
            }
        }
    }


    /**
     * 判断当前的输出变量是否可以覆盖掉历史的输出变量
     *
     * @param exeOutputValue 当前的输出变量
     * @param hisOutValue    历史的输出变量
     * @param enumList       当前的输出变量枚举值列表
     * @return
     */
    private boolean executeOutputReplaceHisOutput(String exeOutputValue, String hisOutValue, String enumList) {

        try {
            int hisIndex = -1;
            int exeIndex = -1;

            JSONArray enumArray = JSONArray.parseArray(enumList);

            for (int i = 0; i < enumArray.size(); i++) {
                // 如果当前的输出变量包含了枚举值，则当前的索引值替换成当前循环的值
                if (exeOutputValue.equals(enumArray.getString(i))) {
                    exeIndex = i;
                } else if (hisOutValue.equals(enumArray.getString(i))) {
                    hisIndex = i;
                }
            }

            // 当索引大于-1证明存在了当前输出变量包含当前枚举值，当exeIndex < hisIndex
            // 证明了当前的输出规则里面的没有包含当前规则输出的枚举值，而历史的有，则可以进行替换
            if (exeIndex > -1 && hisIndex > -1 && exeIndex < hisIndex) {
                return true;
            }

        } catch (Exception e) {
            log.error("parse enumListStr error,enumListStr = {}", enumList);
            return false;
        }
        return false;
    }
}
